package com.lc.gateway.filters;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author liuc
 * @date 2021/6/28
 * @description ip防暴刷
 **/
@Slf4j
@Component
@RefreshScope
public class IpRequestLimitFilter implements GlobalFilter {

    private LimitMap map = new LimitMap();

    @Value("${request.ip.limit.minutes}")
    private int limitMinutes;
    @Value("${request.ip.limit.times}")
    private int limitTimes;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String clientIp = request.getRemoteAddress().getHostString();
        String path = request.getURI().getPath();
        //对注册接口进行防暴控制
        if(path.startsWith("/user/register")){
            if(!map.put(clientIp, System.currentTimeMillis())){
                //返回303
                response.setStatusCode(HttpStatus.FORBIDDEN);
                log.debug("=====>IP:" + clientIp + " 频繁进⾏注册，请求已被拒绝！");
                String data = "您频繁进⾏注册，请求已被拒绝";
                DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
                return response.writeWith(Mono.just(wrap));
            }
        }
        return chain.filter(exchange);
    }

    class LimitMap extends ConcurrentHashMap<String, LinkedList<Long>>{

        public boolean put(String ip, Long lastReqTime){
            LinkedList<Long> longs = get(ip);
            if(longs == null){
                longs = new LinkedList<>();
                put(ip,longs);
            }
            Iterator<Long> iterator = longs.iterator();
            while(iterator.hasNext()){
                Long next = iterator.next();
                long time = lastReqTime - next;
                if(time > limitMinutes * 60 * 1000){
                    iterator.remove();
                }else {
                    break;
                }
            }
            if(longs.size() > limitTimes){
                return false;
            }
            longs.add(lastReqTime);
            return true;
        }
    }
}
